#include "dev_by25q64.h"

uint8_t dev_fls_data_temp[DEV_FLASH_DATA_LEN];

/***********************************************************
*@fuction	:
*@brief		:
*@param		:
*@return	:
*@author	:
*@date		:
***********************************************************/
uint8_t dev_flash_init(void)
{
    spi2_context.config();
}


/***********************************************************
*@fuction	:spi_master_send_recv_byte
*@brief		:
*@param		:
*@return	:
*@author	:
*@date		:
***********************************************************/
extern uint8_t SPI_ReadWriteByte(SPI_TypeDef* SPIx, uint16_t data);

uint8_t spi_master_send_recv_byte(uint8_t sourcedata)
{
#if 0    
    static uint8_t ByteRecv;
    while(RESET == SPI_I2S_GetFlagStatus(SPI2, SPI_FLAG_TBE))
        ;
    SPI_I2S_SendData(SPI2, sourcedata);
    while(RESET == SPI_I2S_GetFlagStatus(SPI2, SPI_FLAG_RBNE))
        ;
    ByteRecv = SPI_I2S_ReceiveData(SPI2);
    return ByteRecv;
#else    
    return SPI_ReadWriteByte(SPI2, sourcedata);
#endif
}


/***********************************************************
*@fuction	:spi_master_recv_some_bytes
*@brief		:
*@param		:
*@return	:
*@author	:
*@date		:
***********************************************************/
void spi_master_recv_some_bytes( uint8_t *sourcedata, uint16_t length)
{
    uint8_t *temp_data = sourcedata;
    while (length--)
    {
        *temp_data = spi_master_send_recv_byte(0xFF);	//发送 0xff 为从设备提供时钟
        temp_data++;
    }
}


/***********************************************************
*@fuction	:dev_fls_write_enable
*@brief		:
*@param		:
*@return	:
*@author	:
*@date		:
***********************************************************/
void dev_fls_write_enable(void)
{
    DEV_FLASH_CS_LOW();
    spi_master_send_recv_byte(BY25Q64_WriteEnable);
    DEV_FLASH_CS_HIGH();
}

/***********************************************************
*@fuction	:dev_fls_read_status_register
*@brief		:
*@param		:
*@return	:
*@author	:
*@date		:
***********************************************************/
uint8_t dev_fls_read_status_register(uint8_t ReadStatusRegisterx)
{
    DEV_FLASH_CS_LOW();
 
    spi_master_send_recv_byte(ReadStatusRegisterx);
    uint8_t StatusRegisterx = spi_master_send_recv_byte(0xFF);
    DEV_FLASH_CS_HIGH();
    return StatusRegisterx;
}

/***********************************************************
*@fuction	:dev_fls_write_status_registerx
*@brief		:写状态寄存器
*@param		:
*@return	:
*@author	:
*@date		:
***********************************************************/
void dev_fls_write_status_registerx(uint8_t sourcedata, uint8_t ReadStatusRegisterx)
{
    dev_fls_write_enable();
    while(((dev_fls_read_status_register(BY25Q64_ReadStatusRegister1)) & 0x01) == 0x01)
    ;
    DEV_FLASH_CS_LOW();
    spi_master_send_recv_byte(ReadStatusRegisterx);
    spi_master_send_recv_byte(sourcedata);
    DEV_FLASH_CS_HIGH();
}

/***********************************************************
*@fuction	:dev_fls_erase_sector
*@brief		:擦除sector
*@param		:
*@return	:
*@author	:
*@date		:
***********************************************************/
void dev_fls_erase_sector(uint32_t eraseAddr)
{
    dev_fls_write_enable();
    while(((dev_fls_read_status_register(BY25Q64_ReadStatusRegister1)) & 0x01) == 0x01)
        ;
    DEV_FLASH_CS_LOW();
    spi_master_send_recv_byte(BY25Q64_SectorErase);
    spi_master_send_recv_byte((uint8_t)(eraseAddr >> 16));
    spi_master_send_recv_byte((uint8_t)(eraseAddr >> 8));
    spi_master_send_recv_byte((uint8_t)(eraseAddr));
    DEV_FLASH_CS_HIGH();
    while(((dev_fls_read_status_register(BY25Q64_ReadStatusRegister1)) & 0x01) == 0x01)
    ;
}

 
/***********************************************************
*@fuction	:dev_fls_erase_appoint_sector
*@brief		:擦除指定范围的sector
*@param		:起始地址到结束地址
*@return	:void
*@author	:
*@date		:
***********************************************************/
void dev_fls_erase_appoint_sector(uint32_t addstart, uint32_t addend)
{
    uint32_t startsector =  addstart / BY25Q64_SectorSize * BY25Q64_SectorSize;
    uint32_t endsector   =  addend / BY25Q64_SectorSize * BY25Q64_SectorSize;
    uint32_t erasesectornum = (endsector / BY25Q64_SectorSize) - (startsector / BY25Q64_SectorSize);
    for(uint8_t i = 0; i <= erasesectornum; i++)
    {
        dev_fls_erase_sector(startsector + i * BY25Q64_SectorSize);
    }
}

/***********************************************************
*@fuction	:dev_fls_erase_chip
*@brief		:整片擦除
*@param		:--
*@return	:void
*@author	:
*@date		:
***********************************************************/
void dev_fls_erase_chip(void)
{
    dev_fls_write_enable();
    while(((dev_fls_read_status_register(BY25Q64_ReadStatusRegister1)) & 0x01) == 0x01)
        ;
    DEV_FLASH_CS_LOW();
    spi_master_send_recv_byte(BY25Q64_ChipErase);
    DEV_FLASH_CS_HIGH();
    while(((dev_fls_read_status_register(BY25Q64_ReadStatusRegister1)) & 0x01) == 0x01)
        ;
}

/***********************************************************
*@fuction	:dev_fls_read_identification
*@brief		:
*@param		:--
*@return	:void
*@author	:
*@date		:
***********************************************************/
unsigned int dev_fls_read_identification(void)
{
    uint32_t Identification = 0;
    uint8_t recv_buff[3] = {0};
 
    DEV_FLASH_CS_LOW();
 
    spi_master_send_recv_byte(BY25Q64_ReadIdentification);	//9fh
 
    uint8_t recvLen = sizeof(recv_buff);
 
    for(uint8_t i = 0 ; i < recvLen; i++)
    {
        recv_buff[i] = spi_master_send_recv_byte(0xFF);	//发送 0xff 为从设备提供时钟
    }
 
    Identification = (recv_buff[0] << 16) | (recv_buff[1] << 8) | (recv_buff[2]);
 
    DEV_FLASH_CS_HIGH();
 
    return Identification;
}

/***********************************************************
*@fuction	:dev_fls_read_id
*@brief		:read device ID
*@param		:
*@return	:
*@author	:
*@date		:
***********************************************************/
unsigned short dev_fls_read_id(void)
{
    uint16_t ID = 0;
    DEV_FLASH_CS_LOW();
 
    spi_master_send_recv_byte(BY25Q64_ManufacturerDeviceID);
    spi_master_send_recv_byte(0x00);
    spi_master_send_recv_byte(0x00);
    spi_master_send_recv_byte(0x00);
 
    ID |= spi_master_send_recv_byte(0xFF) << 8;
    ID |= spi_master_send_recv_byte(0xFF);
 
    DEV_FLASH_CS_HIGH();
 
    return ID;
}

/***********************************************************
*@fuction	:dev_fls_write_page
*@brief		: 页编程，需要考虑 先删除，后写入，写入时要考虑跨页机制
*@param		:--
*@return	:void
*@author	:
*@date		:2023-06-13
***********************************************************/
void dev_fls_write_page(uint8_t *sourcedata, uint32_t address, uint32_t length)
{
    dev_fls_write_enable();
    while(((dev_fls_read_status_register(BY25Q64_ReadStatusRegister1)) & 0x01) == 0x01)
        ;
    DEV_FLASH_CS_LOW();
    spi_master_send_recv_byte(BY25Q64_PageProgram);
    spi_master_send_recv_byte(address >> 16);
    spi_master_send_recv_byte(address >> 8);
    spi_master_send_recv_byte(address >> 0);
    for(uint32_t i = 0; i < length; i++)
    {
        spi_master_send_recv_byte(sourcedata[i]);
    }
 
    DEV_FLASH_CS_HIGH();
    while(((dev_fls_read_status_register(BY25Q64_ReadStatusRegister1)) & 0x01) == 0x01);
    
}

/***********************************************************
*@fuction	:dev_fls_write_data
*@brief		:
*@param		:--
*@return	:void
*@author	:
*@date		:2023-06-22
***********************************************************/
void dev_fls_write_data(uint8_t *sourcedata, uint32_t address, uint32_t length)
{
    uint32_t currentGroup = 0;
    uint32_t DataAdd = address;
    uint32_t currentLen = length, residuelength = length;
    uint32_t shiftingadd = address % BY25Q64_PageSize;
    uint32_t firstwritelength = 0;
    uint8_t status1 = dev_fls_read_status_register(BY25Q64_ReadStatusRegister1);
    uint8_t status2 = dev_fls_read_status_register(BY25Q64_ReadStatusRegister2);
    uint8_t status3 = dev_fls_read_status_register(BY25Q64_ReadStatusRegister3);
    //我用得模式为CMP = 0 ，status1 = 0,status2=0;所对应的为没有数据保护
    //vgd25q64WriteStatusRegisterx(0x04, GD25Q64_ReadStatusRegister1);//GD25Q64_WriteStatusRegister1这一块似乎存在问题
    //status1 = ucgd25q64ReadStatusRegister(GD25Q64_ReadStatusRegister1);
    //write first page
    if(shiftingadd && ((shiftingadd + length) > BY25Q64_PageSize))
    {
        firstwritelength = BY25Q64_PageSize - shiftingadd;
        dev_fls_write_enable();
        while(((dev_fls_read_status_register(BY25Q64_ReadStatusRegister1)) & 0x01) == 0x01)
            ;
        DEV_FLASH_CS_LOW();
        spi_master_send_recv_byte(BY25Q64_PageProgram);
        spi_master_send_recv_byte(DataAdd >> 16);
        spi_master_send_recv_byte(DataAdd >> 8);
        spi_master_send_recv_byte(DataAdd >> 0);
        for(uint32_t i = 0; i < firstwritelength; i++)
        {
            spi_master_send_recv_byte(sourcedata[i]);
        }
 
        DEV_FLASH_CS_HIGH();
        while(((dev_fls_read_status_register(BY25Q64_ReadStatusRegister1)) & 0x01) == 0x01)
            ;
 
        DataAdd += firstwritelength;
        residuelength -= firstwritelength;
        currentLen = residuelength;
    }
		
    do
    {
        //dev_fls_read_data(gd25q64Data,PosGD25Q64_TaskInfo_num_I,GD25Q64_SectorSize);
        if(currentLen > (BY25Q64_PageSize))
        {
            currentLen = BY25Q64_PageSize;
        }
        dev_fls_write_enable();
        while(((dev_fls_read_status_register(BY25Q64_ReadStatusRegister1)) & 0x01) == 0x01)
            ;
        DEV_FLASH_CS_LOW();
        spi_master_send_recv_byte(BY25Q64_PageProgram);
        spi_master_send_recv_byte(DataAdd >> 16);
        spi_master_send_recv_byte(DataAdd >> 8);
        spi_master_send_recv_byte(DataAdd >> 0);
        for(uint32_t i = 0; i < currentLen; i++)
        {
            uint32_t shifting = firstwritelength + i + (currentGroup * BY25Q64_PageSize);
            spi_master_send_recv_byte(sourcedata[shifting]);
        }
 
        DEV_FLASH_CS_HIGH();
        while(((dev_fls_read_status_register(BY25Q64_ReadStatusRegister1)) & 0x01) == 0x01)
            ;
        if(residuelength >= currentLen)
        {
            residuelength -= currentLen;
        }
        DataAdd += currentLen;
        currentLen = residuelength;
    }
    while((currentGroup++) < (length / (BY25Q64_PageSize)));
}

/***********************************************************
*@fuction	:
*@brief		:
*@param		:
*@return	:
*@author	:
*@date		:
***********************************************************/
void dev_fls_read_data(uint8_t *sourcedata, uint32_t address, uint32_t length)
{
    uint8_t *temp_data = sourcedata;
 
    DEV_FLASH_CS_LOW();
 
    spi_master_send_recv_byte(BY25Q64_ReadData);
    spi_master_send_recv_byte((uint8_t)(address >> 16));
    spi_master_send_recv_byte((uint8_t)(address >> 8));
    spi_master_send_recv_byte((uint8_t)(address >> 0));
    while (length--)
    {
        *temp_data = spi_master_send_recv_byte(0xFF);	//发送 0xff 为从设备提供时钟
        temp_data++;
    }
 
    DEV_FLASH_CS_HIGH();
}


/***********************************************************
*@fuction	:
*@brief		:
*@param		:
*@return	:
*@author	:
*@date		:
***********************************************************/
void dev_fls_flush_write(uint8_t *sourcedata, uint32_t address, uint32_t length)
{
    BOOL falg = FALSE;
    
    uint32_t startSectorAddress = ((address / BY25Q64_SectorSize) * BY25Q64_SectorSize);
    uint32_t endSectorAddress   = (((address + length) / BY25Q64_SectorSize) * BY25Q64_SectorSize);
    uint32_t startAddress = address % BY25Q64_SectorSize;
    dev_fls_read_data(dev_fls_data_temp, startSectorAddress, BY25Q64_SectorSize);
 
    for(uint32_t i = 0; i < length; i++)
    {
        if(dev_fls_data_temp[startAddress + i] != 0xFF)
        {
            falg = TRUE;
        }
    }
    //no need to erase
    if(!falg)
    {
        dev_fls_write_data(sourcedata, address, length);
    }
    else
    {
        //need to erase
        if(startSectorAddress == endSectorAddress)
        {
            for(uint32_t i = 0; i < length; i++)
            {
                dev_fls_data_temp[i + startAddress] = sourcedata[i];
            }
            dev_fls_erase_sector(address);
            dev_fls_write_data(dev_fls_data_temp, startSectorAddress, BY25Q64_SectorSize);
        }
        else
        {
            for(uint32_t i = 0; i < (BY25Q64_SectorSize - startAddress); i++)
            {
                dev_fls_data_temp[i + startAddress] = sourcedata[i];
            }
            dev_fls_erase_sector(startSectorAddress);
            dev_fls_write_data(dev_fls_data_temp, startSectorAddress, BY25Q64_SectorSize);
 
            uint32_t enddatalen = length - (BY25Q64_SectorSize - startAddress);
            dev_fls_read_data(dev_fls_data_temp, endSectorAddress, BY25Q64_SectorSize);
            for(uint32_t i = 0; i < enddatalen; i++)
            {
                dev_fls_data_temp[i] = sourcedata[(BY25Q64_SectorSize - startAddress) + i];
            }
            dev_fls_erase_sector(endSectorAddress);
            dev_fls_write_data(dev_fls_data_temp, endSectorAddress, BY25Q64_SectorSize);
        }
    }
}

/***********************************************************
*@fuction	:dev_fls_flush_del
*@brief		:
*@param		:--
*@return	:void
*@author	:--
*@date		:2023-04-23
***********************************************************/
void dev_fls_flush_del(uint32_t address, uint32_t length)
{
    uint32_t startSectorAddress = ((address / BY25Q64_SectorSize) * BY25Q64_SectorSize);
    uint32_t endSectorAddress = (((address + length) / BY25Q64_SectorSize) * BY25Q64_SectorSize);
    uint32_t SectorinterstartAddress = (address - startSectorAddress);
    if(startSectorAddress == endSectorAddress)
    {
        dev_fls_read_data(dev_fls_data_temp, startSectorAddress, BY25Q64_SectorSize);
        dev_fls_erase_sector(startSectorAddress);
 
        //write Previous
        dev_fls_write_data(dev_fls_data_temp, startSectorAddress, SectorinterstartAddress);
 
        //write subsequent
        dev_fls_write_data(dev_fls_data_temp + SectorinterstartAddress + length, startSectorAddress + SectorinterstartAddress + length, BY25Q64_SectorSize - SectorinterstartAddress - length);
 
    }
    else
    {
        //startSectorAddress
        dev_fls_read_data(dev_fls_data_temp, startSectorAddress, BY25Q64_SectorSize);
        dev_fls_erase_sector(startSectorAddress);
        dev_fls_write_data(dev_fls_data_temp, startSectorAddress, SectorinterstartAddress);
 
        //endSectorAddress
        dev_fls_read_data(dev_fls_data_temp, endSectorAddress, BY25Q64_SectorSize);
        dev_fls_erase_sector(endSectorAddress);
        uint32_t endSectorWriteAdd = (length + SectorinterstartAddress) % BY25Q64_SectorSize ;
        dev_fls_write_data(&dev_fls_data_temp[endSectorWriteAdd], endSectorAddress + endSectorWriteAdd, BY25Q64_SectorSize - endSectorWriteAdd);
 
        //middleSector
        uint32_t middleSector = address + BY25Q64_SectorSize;
        for( ; middleSector < endSectorAddress; middleSector += BY25Q64_SectorSize)
        {
            dev_fls_erase_sector(middleSector);
        }
    }
}

/***********************************************************
*@fuction	:dev_fls_get_data_pos
*@brief		:input address then output this data storage position
*@param		:uint32_t Startaddress  uint32_t length
*@return	:uint32_t position
*@author	:
*@date		:2023-04-23
***********************************************************/
uint32_t dev_fls_get_data_pos(uint32_t Startaddress, uint32_t length)
{
    uint32_t position = 0;
    uint32_t tempPos;
    uint32_t count = 0;
    BOOL direction = FALSE;
    dev_fls_read_data(dev_fls_data_temp, Startaddress, length);
    tempPos = length;
    uint8_t checkdata[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
    do
    {
        tempPos /= 2;
        if(direction)
        {
            position -= tempPos;
        }
        else
        {
            position += tempPos;
        }
        count++;
        if(1)//vdatastorageCheckArraySame(checkdata, &gd25q64Data[position], 8))
        {
            //data is front
            direction = TRUE;
        }
        else
        {
            //data is back
            direction = TRUE;
        }
    }
    while(tempPos > 0);
    position++;
 
    while(dev_fls_data_temp[position] == 0xFF)
    {
        position--;
    }
    position++;
 
    return (position + Startaddress);
}

uint16_t ManufactureID=0;

/***********************************************************
*@fuction	:BY25Q64_Debug
*@brief		:
*@param		:
*@return	:
*@author	:
*@date		:
***********************************************************/
void BY25Q64_Debug(void)
{
    static uint8_t init=0;
    
    if(0==init)
    {
        init=1;
        dev_flash_init();
    }

    if(init)
    {
        ManufactureID = dev_fls_read_id();  
    }
}
